import asyncio
import heapq
from typing import Any, Awaitable, Callable

from .utils import printime


class Manager:
    def __init__(self, run_func: Callable[[Any], Awaitable[None]], run_count: int, delay: int):
        self.__infos = []
        self.__run_func = run_func  # 任务执行的方法
        self.__run_count = run_count  # 同时执行任务数量
        self.__delay = delay  # 任务之间的最短延迟
        self.__runing_count = 0  # 执行中的数
        self.__success_count = 0  # 成功数量
        self.__fail_count = 0  # 失败数量
        self.__isrun = False  # 运行状态

    def add(self, info: Any, priority=0):
        heapq.heappush(self.__infos, (priority, info))

    def run(self):
        self.__isrun = True
        asyncio.run(self.__run())

    async def __run(self):
        tasks = [asyncio.create_task(self.__runTask()) for _ in range(self.__run_count)]
        tasks.append(asyncio.create_task(self.__printTask()))
        await asyncio.gather(*tasks)

    async def __runTask(self):
        while self.__isrun and (self.__runing_count > 0 or len(self.__infos) > 0):
            # 等待
            if len(self.__infos) == 0:
                await asyncio.sleep(1)
                continue
            elif self.__delay > 0:
                await asyncio.sleep(self.__delay)

            self.__runing_count += 1
            if await self.__run_func(heapq.heappop(self.__infos)[1]):
                self.__success_count += 1
            else:
                self.__fail_count += 1
            self.__runing_count -= 1

    async def __printTask(self):
        while self.__runing_count > 0 or (self.__isrun and len(self.__infos) > 0):
            self.__printInfo()
            await asyncio.sleep(0.1)

        if self.hasrun():
            self.__printInfo()
            print("")

    def hasrun(self):
        return self.__runing_count > 0 or self.__success_count > 0 or self.__fail_count > 0

    def isfinish(self):
        return len(self.__infos) == 0 and self.hasrun()

    def stop(self):
        self.__isrun = False

    def __printInfo(self):
        printime(
            " ".join(
                [
                    f"isrun={self.__isrun}",
                    f"runing={self.__runing_count}/{self.__run_count}",
                    f"left={len(self.__infos)}",
                    f"success={self.__success_count}",
                    f"fail={self.__fail_count}",
                    f"finish={self.__success_count + self.__fail_count}",
                    f"all={self.__runing_count + len(self.__infos) + self.__success_count + self.__fail_count}",
                ]
            ),
            end="\r",
        )
